/*
 * Copyright 2000-2005 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.jetbrains.idea.accurev.application.annotation;

import com.intellij.openapi.editor.EditorGutterAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.annotate.AnnotationListener;
import com.intellij.openapi.vcs.annotate.FileAnnotation;
import com.intellij.openapi.vcs.annotate.LineAnnotationAspect;
import com.intellij.openapi.vfs.*;
import com.intellij.util.text.SyncDateFormat;
import org.jetbrains.idea.accurev.actions.ShowAllSubmittedFilesAction;
import org.jetbrains.idea.accurev.accurev.P4Revision;
import org.jetbrains.idea.accurev.accurev.connections.P4Connection;
import org.jetbrains.idea.accurev.accurev.connections.AccuRevConnectionManager;
import org.jetbrains.annotations.Nullable;

import java.awt.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;

public class PerforceFileAnnotation implements FileAnnotation {
  private final AnnotationInfo myAnnotationInfo;
  private final List<AnnotationListener> myListeners = new ArrayList<AnnotationListener>();
  private final P4Revision[] myRevisions;
  private VirtualFile myFile;

  private final LineAnnotationAspect REVISION = new PerforceRevisionAnnotationAspect();

  private final LineAnnotationAspect CLIENT = new LineAnnotationAspect() {
    public String getValue(int lineNumber) {
      P4Revision p4Revision = findRevisionForLine(lineNumber);
      if (p4Revision != null) {
        return p4Revision.getUser();
      } else {
        return "";
      }
    }

  };

  private final LineAnnotationAspect DATE = new LineAnnotationAspect() {
    public String getValue(int lineNumber) {
      P4Revision p4Revision = findRevisionForLine(lineNumber);
      if (p4Revision != null) {
        return DATE_FORMAT.format(p4Revision.getDate());
      } else {
        return "";
      }
    }

  };

  private final VirtualFileAdapter myListener;
  private static final SyncDateFormat DATE_FORMAT = new SyncDateFormat(SimpleDateFormat.getDateInstance(SimpleDateFormat.SHORT));
  private final Project myProject;


  public PerforceFileAnnotation(final AnnotationInfo annotationInfo,
                                final VirtualFile file,
                                P4Revision[] revisions,
                                final Project project) {
    myAnnotationInfo = annotationInfo;
    myRevisions = revisions;
    myProject = project;
    myFile = file;

    myListener = new VirtualFileAdapter() {
      public void propertyChanged(VirtualFilePropertyEvent event) {

        if (file != event.getFile()) return;
        if (!event.isFromRefresh()) return;

        if (event.getPropertyName().equals(VirtualFile.PROP_WRITABLE)) {
          if (((Boolean)event.getOldValue()).booleanValue()) {
            fireAnnotationChanged();
          }
        }
      }

      public void contentsChanged(VirtualFileEvent event) {
        if (file != event.getFile()) return;
        if (!event.isFromRefresh()) return;
        if (!file.isWritable()) {
          fireAnnotationChanged();
        }
      }

      private void fireAnnotationChanged() {
        final AnnotationListener[] listeners = myListeners.toArray(new AnnotationListener[myListeners.size()]);
        for (AnnotationListener listener : listeners) {
          listener.onAnnotationChanged();
        }
      }
    };

    VirtualFileManager.getInstance().addVirtualFileListener(myListener);

  }

  public void addListener(AnnotationListener listener) {
    myListeners.add(listener);
  }

  public void removeListener(AnnotationListener listener) {
    myListeners.add(listener);
  }

  public void dispose() {
    VirtualFileManager.getInstance().removeVirtualFileListener(myListener);
  }

  public LineAnnotationAspect[] getAspects() {
    return new LineAnnotationAspect[]{REVISION, DATE, CLIENT};
  }

  public String getToolTip(final int lineNumber) {
    P4Revision revision = findRevisionForLine(lineNumber);
    return revision != null ? "Revision " + revision.getChangeNumber() + ": " + revision.getSubmitMessage() : "";
  }

  public String getAnnotatedContent() {
    return myAnnotationInfo.getContent();
  }

  @Nullable
  private P4Revision findRevisionForLine(final int lineNumber) {
    final int revision = myAnnotationInfo.getRevision(lineNumber);
    for (P4Revision p4Revision : myRevisions) {
      if (myAnnotationInfo.isUseChangelistNumbers()) {
        if (p4Revision.getChangeNumber() == revision) return p4Revision;
      }
      else {
        if (p4Revision.getRevisionNumber() == revision) return p4Revision;
      }
    }
    return null;
  }

  private class PerforceRevisionAnnotationAspect implements LineAnnotationAspect, EditorGutterAction {
    public String getValue(int lineNumber) {
      P4Revision p4Revision = findRevisionForLine(lineNumber);
      if (p4Revision != null) {
        return String.valueOf(p4Revision.getChangeNumber());
      } else {
        return "";
      }
    }

    public Cursor getCursor(final int lineNum) {
      return Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
    }

    public void doAction(int lineNum) {
      P4Revision p4Revision = findRevisionForLine(lineNum);
      if (p4Revision != null) {
        final long changeNumber = p4Revision.getChangeNumber();
        P4Connection connection = AccuRevConnectionManager.getInstance(myProject).getConnectionForFile(myFile);
        ShowAllSubmittedFilesAction.showAllSubmittedFiles(myProject, changeNumber,
                                                          p4Revision.getSubmitMessage(),
                                                          p4Revision.getDate(),
                                                          p4Revision.getUser(),
                                                          connection);
      }
    }
  }
}
